using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text; // for TextRenderingHint
using System.Globalization; // for hex number styles, as used in color attribute values
using System.IO;
using System.Resources;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Xml;

[assembly: System.CLSCompliant(true)]
[assembly: System.Runtime.InteropServices.ComVisible(false)]
[assembly: System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.RequestMinimum, UnmanagedCode=true)]

namespace FB.WinForms.Controls
{
	/// <summary>
	/// Represents an extended ComboBox control that can display simplified HTML and raises an event
	/// when a link is clicked.
	/// </summary>
	/// <remarks>
	/// A <c>HtmlDropDownList</c> displays a drop-down list style <see cref="ComboBox"/>,
	/// where the text portion is not editable but you must click the drop-down arrow
	/// to show the list box portion of the control (A standard drop-down list style
	/// <see cref="ComboBox"/> will display the list box portion if the text portion
	/// of the control is clicked).
	/// <para/>
	/// The HTML used must conform to the stricter XML rules (i.e. be well-formed HTML).
	/// Exceptions to the XML rules are; there is no need for a single root tag as one is
	/// generated by the control and the tags and attributes are not case sensitive.  The
	/// XML rules that do apply are; all tags must be closed, this relates mainly to the IMG tag, 
	/// No tags can overlap, (i.e. &lt;B&gt;Bold &lt;I&gt;Bold and Italic&lt;/B&gt; Italic&lt;/I&gt; is invalid)
	/// and all attributes must be surrounded by single or double quotation marks.
	/// <para/>
	/// The level of support for HTML is very limited, the following tags are supported.  
	/// There is however an additional BRUSH tag.
	/// <list type="table">
	/// <listheader>
	/// <term>Tag</term>
	/// <term>Description</term>
	/// </listheader>
	/// <item>
	/// <description>A</description>
	/// <description>Anchor: Suported attribute: id.
	/// <para/>
	/// The <c>id</c> attribute value is used to identify the link in the <see cref="LinkClicked"/> event
	/// and the methods <see cref="ReplaceInnerText"/> and <see cref="ReplaceInnerHtml"/>.
	/// <code>&lt;a id="size"&gt;size&lt;/a&gt;</code>
	/// </description>
	/// </item>
	/// <item>
	/// <description>B</description>
	/// <description>
	/// Bold font.
	/// <para/>
	/// <code>&lt;b&gt;text&lt;/b&gt;</code>
	/// </description>
	/// </item>
	/// <item>
	/// <description>BRUSH</description>
	/// <description>Brush: Supported attributes: type, shadowcolor, startingcolor, endingcolor,
	/// gradientangle, offset
	/// <para/>
	/// The values for the <c>type</c> attribute can be "LinearGradient" or "Shadow".
	/// <para/>
	/// For a "LinearGradient" brush the attribute <c>startingcolor</c> represents the starting
	/// color for the gradient, <c>endingcolor</c> represents the ending color for the gradient
	/// and <c>gradientangle</c> represents the gradient's orientation line angle.
	/// <para/>
	/// <example>This example shows a linear gradient brush from Red to Blue from left to right.
	/// <code>
	/// &lt;brush type="LinearGradient" StartingColor="Red" EndingColor="Blue" GradientAngle="0"&gt;&lt;brush&gt; 
	/// </code>
	/// </example>
	/// For a "Shadow" brush the attribute <c>offset</c> represents the size of the shadow and 
	/// <c>shadowcolor</c> the color of the shadow.
	/// <para/>
	/// <example>This example shows a Shadow brush with an offset of two pixels and a shadow color of Blue.
	/// <code>
	/// &lt;brush type="Shadow" offset="2" shadowcolor="Blue"&gt;&lt;brush&gt;
	/// </code>
	/// </example>
	/// <note>
	/// This is not a standard HTML tag.
	/// </note>
	/// </description>
	/// </item>
	/// <item>
	/// <description>FONT</description>
	/// <description>Font: Supported attributes: color, font.
	/// <para/>
	/// <example>This example shows the color attribute specifing known named color.
	/// <code>
	/// &lt;font color="blue"/&gt;&lt;/font&gt; 
	/// </code>
	/// </example>
	/// <example>This example shows the color attribute specifing an RGB value.
	/// <code>
	/// &lt;font color="#800080"/&gt;&lt;/font&gt; 
	/// </code>
	/// <note>The RGB value must be six hexidecimal digits.</note>
	/// </example>
	/// <para/>
	/// The <c>font</c> attribute value is the string representation of the FontFamily
	/// object for the new Font object as used in the <see cref="Font(string ,float ,FontStyle)"/>
	/// constructor.  Invalid values we be ignored and the controls <see cref="Font"/> 
	/// property will be used.
	/// <para/><example>This example shows the face attribute specifing a "Courier New" font.
	/// <code>
	/// &lt;font face="courier new"/&gt;&lt;/font&gt; 
	/// </code>
	/// </example>
	/// </description>
	/// </item>
	/// <item>
	/// <description>I</description>
	/// <description>
	/// Italic font.
	/// <para/>
	/// <code>&lt;i&gt;text&lt;/i&gt;</code>
	/// </description>
	/// </item>
	/// <item>
	/// <description>IMG</description>
	/// <description>
	/// Image: Supported attributes: src.
	/// <para/>
	/// The <c>src</c> attribute can specify a filename or index into <see cref="ImageList"/>.
	/// <para/>
	/// <example>This example shows the src attribute specifing a filename.
	/// <code>
	/// &lt;img src="images/blue.bmp"/&gt; 
	/// </code>
	/// </example>
	/// <example>This example shows the src attribute specifing an index into <see cref="ImageList"/>.
	/// <code>
	/// &lt;img src="#1"/&gt; 
	/// </code>
	/// </example>
	/// </description>
	/// </item>
	/// <item>
	/// <description>S</description>
	/// <description>
	/// Strikeout.
	/// <para/>
	/// <code>&lt;s&gt;text&lt;/s&gt;</code>
	/// </description>
	/// </item>
	/// <item>
	/// <description>U</description>
	/// <description>
	/// Underline.
	/// <para/>
	/// <code>&lt;u&gt;text&lt;/u&gt;</code>
	/// </description>
	/// </item>
	/// </list>
	/// <para/>
	/// The color attribute values can specify a GDI+ color name or an RGB value.  Invalid values
	/// we be ignored and the controls <see cref="Control.ForeColor"/> property will be used.
	/// <note>
	/// The GDI+ known colors are defined by <see cref="Color.IsKnownColor "/> and are
	/// the same as HTML color names.  The GDI+ system colors are defined by <see cref="Color.IsSystemColor"/>
	/// and are different from HTML Color names. For example you need to use the GDI+ ActiveCaptionText
	/// value not the HTML CaptionText.
	/// </note>
	/// The only HTML character entity references (e.g. <![CDATA[&nbsp;]]>, <![CDATA[&copy;]]> and <![CDATA[&reg;]]> )
	/// that are handled correctly are <![CDATA[&amp;]]>, <![CDATA[&quot;]]>, <![CDATA[&apos;]]>, <![CDATA[&lt;]]>,
	/// and <![CDATA[&gt;]]> for the characters &amp;, &quot;, &apos;, &lt;, and &gt; respectively as these are
	/// automatically handled by the XmlTextReader class.  Numeric character entity references are also supported by
	/// XmlTextReader so <![CDATA[&#x20AC;]]> will display the euro currency symbol, &#x20AC;.
	/// <para/>
	/// In addition to <see cref="ComboBox"/> functionality the <c>HtmlDropDownList</c> also provides
	/// features to handle the HTML content.  The event <see cref="LinkClicked"/> is raised when
	/// a link is clicked.  The <see cref="LinkClicked"/> event includes the <see cref="LinkClickedEventArgs"/>
	/// that defines the link <c>id</c>, <c>InnerText</c> and <c>InnerHtml</c> of the link.
	/// The methods <see cref="ReplaceInnerText"/> and <see cref="ReplaceInnerHtml"/> allow the
	/// <c>InnerText</c> and <c>InnerHtml</c>, respectively to be updated.
	/// The properties <see cref="BaseHref"/> allows the base path for images to be specified.  
	/// An <see cref="ImageList"/> has been added to allow images to be specified by index.
	/// The <see cref="LinkColor"/> allows the default color of the text marking links to be
	/// specified.  The <see cref="SelectedIndex"/> allows the selected item to be selected at
	/// design time.  
	/// <para/>
	/// If the text to be displayed involves different styles of font (e.g. Italic, Bold, ...)
	/// then extra space may appear between the different pieces of text.  This will be especially
	/// noticable at smaller font sizes as the effects of font scaling and grid fitting become 
	/// more noticeable.  The default rendering is grid-fitted if the text spacing is not accurate
	/// enough you can specifiy a value a <see cref="System.Drawing.Text.TextRenderingHint.AntiAlias"/>
	/// in the <see cref="TextRenderingHint"/> property.  This will avoid grid fitting and correct
	/// the spacing but can make the text appear grayish.
	/// <note>
	/// The default rendering on Windows XP can be changed under the Display Properties, Appearance tab
	/// click the Effects... button then select an option from the "Use the following method to smooth
	/// edges of screen fonts:" drop down list.
	/// </note>
	/// <para/>
	/// Some properties of the <see cref="ComboBox"/> that have fixed values in the <c>HtmlDropDownList</c>
	/// have been hidden from the designer and the editor.  These are the <see cref="AllowDrop"/>,
	/// <see cref="DrawMode"/> and <see cref="DropDownStyle"/>.
	/// </remarks>
	[ToolboxBitmap(typeof(HtmlDropDownList), "HtmlDropDownList")]
	public class HtmlDropDownList : ComboBox
	{
		#region Delegates

		/// <summary>
		/// Represents the method that will handle the <c>LinkClicked</c> event
		/// of a <c>HtmlDropDownList</c>.
		/// </summary>
		/// <param name="sender">The source of the event, an <see cref="HtmlDropDownList"/> object.</param>
		/// <param name="e">A <see cref="HtmlDropDownList.LinkClickedEventArgs"/> that contains the event data.</param>
		public delegate void HtmlDropDownListLinkClickedEventHandler(Object sender, HtmlDropDownList.LinkClickedEventArgs e);

		#endregion

		#region Events

		/// <summary>
		/// Occurs when a link is clicked.
		/// </summary>
		[Category("HtmlDropDownList"),	Description("Occurs when a link has been clicked.")]
		public event HtmlDropDownListLinkClickedEventHandler LinkClicked;

		#endregion

		#region Fields
		/// <summary>
		/// The <c>WM_LBUTTONDOWN</c> message is posted when the user presses the left mouse button
		/// while the cursor is in the client area the control.
		/// </summary>
		private const int WM_LBUTTONDOWN = 0x0201;

		/// <summary>
		/// Contains the base path for images.
		/// </summary>
		private string mBaseHref = string.Empty;

		/// <summary>
		/// Set when the Mouse moves over an &lt;A&gt; tag and used to determine if a <see cref="LinkClicked"/>
		/// event needs to be raised on a left mouse button click.
		/// </summary>
		private LinkRegion mCurrentLinkRegion;

		/// <summary>
		/// Represents the SolidBrush used for drawing the text. Declared at the class level
		/// in an attempt to improve speed.
		/// </summary>
		private SolidBrush			mForeColorBrush;

		/// <summary>
		/// Define the <c>Bounds</c> of the links and there <c>id</c> values for the SelectedItem.
		/// </summary>
		private LinkRegion[]		mHtmlLinkRegions;

		/// <summary>
		/// A class level NameTable to attempt to speed up the XML/HTML reading.
		/// </summary>
		private NameTable			mHtmlNameTable;

		/// <summary>
		/// Represents the default color of text marking links.
		/// </summary>
		private Color				mLinkColor = Color.Blue;

		/// <summary>
		/// Represents the ImageList to use when displaying images in the control.
		/// </summary>
		private ImageList			mImageList;

		/// <summary>
		/// Represents the <see cref="TextRenderingHint"/> used in text layout.
		/// </summary>
		private TextRenderingHint	mTextRenderingHint = TextRenderingHint.SystemDefault;

		#endregion

		#region Constructor

		/// <summary>
		/// Initializes a new instance of the <c>HtmlDropDownList</c> class.
		/// </summary>
		/// <remarks>
		/// Ensures that the <see cref="DrawMode"/> is set to <see cref=" System.Windows.Forms.DrawMode.OwnerDrawFixed"/> and
		/// the <see cref="DropDownStyle"/> is set to <see cref=" System.Windows.Forms.ComboBoxStyle.DropDownList"/>.
		/// </remarks>
		public HtmlDropDownList()
		{
			base.DrawMode = DrawMode.OwnerDrawFixed;
			base.DropDownStyle = ComboBoxStyle.DropDownList;
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets or sets a value indicating whether the control can accept data that the
		/// user drags onto it.
		/// </summary>
		/// <remarks>
		/// Hide this property from the Property Grid and the Editor.
		/// </remarks>
		[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
		public override bool AllowDrop
		{
			get	{ return base.AllowDrop; }
		}
	
		/// <summary>
		/// Gets or sets the base path for image files used in the <c>img</c> tag.
		/// </summary>
		[Category("HtmlDropDownList"), Description("The base path for images."),
		 DefaultValue(""),
		 Editor(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(System.Drawing.Design.UITypeEditor))]
		public string BaseHref
		{
			get { return mBaseHref; }
			set { mBaseHref = value; }
		}

		/// <summary>
		/// Gets or sets a <see cref="LinkRegion"/>, set when the Mouse moves over
		/// an &lt;A&gt; tag and used to determine if a <see cref="LinkClicked"/> event needs to be
		/// raised on a left mouse button click.
		/// </summary>
		private LinkRegion CurrentLinkRegion
		{
			get { return mCurrentLinkRegion; }
			set { mCurrentLinkRegion = value; }
		}

		/// <summary>
		/// Gets or sets the cursor that is displayed when the mouse pointer is over the control.
		/// </summary>
		/// <remarks>
		/// Hide this property from the Property Grid and the Editor as we use it ourselves.
		/// </remarks>
		[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
		public override Cursor Cursor
		{
			get { return base.Cursor; }
		}

		/// <summary>
		/// Specifies how the elements of a control are drawn.
		/// </summary>
		/// <remarks>
		/// As we are only allowing OwnerDrawFixed hide this property from the 
		/// Property Grid and the Editor.
		/// <para/>
		/// The <see cref="DefaultValueAttribute"/> is used to avoided the line generated in the 
		/// <c>InitializeComponent</c> method of the client code.
		/// </remarks>
		[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
		[DefaultValue(typeof(DrawMode), "OwnerDrawFixed")]
		public new DrawMode DrawMode
		{
			get { return base.DrawMode; }
		}

		/// <summary>
		/// Gets or sets a value specifying the style of the combo box.
		/// </summary>
		/// <remarks>
		/// As we are only allowing DropDownList hide this property from the 
		/// Property Grid and the Editor.
		/// <para/>
		/// The <see cref="DefaultValueAttribute"/> is used to avoided the line generated in the 
		/// <c>InitializeComponent</c> method of the client code.
		/// </remarks>
		[Browsable(false), EditorBrowsable(EditorBrowsableState.Never)]
		[DefaultValue(typeof(ComboBoxStyle), "DropDownList")]
		public new ComboBoxStyle DropDownStyle 
		{
			get { return base.DropDownStyle; }
		}

		/// <summary>
		/// Gets or sets the <see cref="SolidBrush"/> used for drawing the text.
		/// </summary>
		/// <remarks>
		/// Declared at the class level so we don't need to re-create it for every
		/// item we draw.
		/// </remarks>
		protected SolidBrush ForeColorBrush
		{
			get { return mForeColorBrush; }
			set { mForeColorBrush = value; }
		}

		/// <summary>
		/// Gets an array of <see cref="LinkRegion"/> objects that define the links currently displayed 
		/// in the edit area of the control.
		/// </summary>
		private LinkRegion[] LinkRegions
		{
			get { return mHtmlLinkRegions; }
		}

		/// <summary>
		/// Gets a class level NameTable to attempt to speed up the XML/HTML reading.
		/// </summary>
		private NameTable HtmlNameTable
		{
			get 
			{
				if (mHtmlNameTable == null)
				{
					mHtmlNameTable = new NameTable();
					// Initialise the NameTable with some common tags.  Timings of the
					// XmlTextReader vary widely but this does seem to speed things up.
					mHtmlNameTable.Add("a");
					mHtmlNameTable.Add("b");
					mHtmlNameTable.Add("brush");
					mHtmlNameTable.Add("color");
					mHtmlNameTable.Add("face");
					mHtmlNameTable.Add("font");
					mHtmlNameTable.Add("html");
					mHtmlNameTable.Add("id");
					mHtmlNameTable.Add("img");
					mHtmlNameTable.Add("src");
				}
				return mHtmlNameTable;
			}
		}

		/// <summary>
		/// Gets or sets the ImageList to use when displaying images in the control.
		/// </summary>
		[Category("HtmlDropDownList"), Description("The ImageList to use when displaying images in the control.")] 
		public ImageList ImageList
		{
			get 
			{ 
				if (mImageList == null)
				{
					mImageList = new ImageList();
				}
				return mImageList; 
			}
			set { mImageList = value; }
		}

		/// <summary>
		/// Gets or sets a collection of the items contained in this HtmlDropDownList.
		/// </summary>
		[Editor(typeof(HtmlDropDownListItemsEditor), typeof(System.Drawing.Design.UITypeEditor))]
		public new ComboBox.ObjectCollection Items
		{
			get { return base.Items; }
			set 
			{ 
				// The Items property is read/write to allow the property editor to update the
				// collection.  The base class Items property is read-only so we need to add 
				// the items one at a time.
				int lSelectedIndex = SelectedIndex;
				base.Items.Clear();
				foreach (object lObject in value)
				{
					base.Items.Add(lObject); 
				}
				// Restore the SelectedIndex which will have been set to -1 by the Clear method.
				try
				{
					SelectedIndex = lSelectedIndex;
				}
				catch (System.ArgumentOutOfRangeException)
				{
					SelectedIndex = -1;
				}
			}
		}

		/// <summary>
		/// Gets or sets the default color of text marking links.
		/// </summary>
		[Category("HtmlDropDownList"), Description("The default color of text marking links.")] 
		[DefaultValue(typeof(Color), "Blue")]
		public Color LinkColor
		{
			get { return mLinkColor; }
			set { mLinkColor = value; }
		}

		/// <summary>
		/// Gets or sets the index specifying the currently selected item.
		/// </summary>
		/// <remarks>
		/// Allow this to be visible and serialized by the designer.
		/// </remarks>
		[Category("HtmlDropDownList"), Description("Gets or sets the index specifying the currently selected item.")]
		[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		[DefaultValue(-1)]
		public override int SelectedIndex
		{
			get { return base.SelectedIndex; }
			set { base.SelectedIndex = value; }
		}

		/// <summary>
		/// Gets or sets the <see cref="TextRenderingHint"/> used in text layout.
		/// </summary>
		[Category("HtmlDropDownList"), Description("Specifies the quality of text rendering.")] 
		[DefaultValue(typeof(TextRenderingHint), "SystemDefault")]
		public TextRenderingHint TextRenderingHint
		{
			get { return mTextRenderingHint; }
			set { mTextRenderingHint = value; }
		}

		#endregion
		
		#region Methods

		/// <summary>
		/// Release the <see cref="ForeColorBrush"/> used in drawing the text.
		/// </summary>
		/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources;
		/// <c>false</c> to release only unmanaged resources.</param>
		protected override void Dispose( bool disposing )
		{
			if (ForeColorBrush != null)
			{
				ForeColorBrush.Dispose();
			}
			base.Dispose(disposing);
		}

		/// <summary>
		/// Handles the <see cref="ComboBox.DrawItem"/> event as the <see cref="DrawMode"/> 
		/// is set to OwnerDrawFixed.
		/// </summary>
		/// <param name="die">A <see cref="DrawItemEventArgs"/> that contains the event data.</param>
		protected override void OnDrawItem(DrawItemEventArgs die)
		{
			if (ForeColorBrush == null)
			{
				// Initialise the ForeColorBrush using the ForeColor specified in the
				// DrawItemEventArgs.
				ForeColorBrush = new SolidBrush(die.ForeColor);
			}
			else
			{
				// Allow for changes in the ForeColor.
				ForeColorBrush.Color = die.ForeColor;
			}

			// Draw the items in the edit area or in the drop down area.  The drawing
			// is the same in both but we don't want the background highlight color drawn
			// for the edit area.  This is blue by default and spoils the rendering of
			// hyperlinks.
			if (die.Index >= 0)
			{
				if (((die.State & DrawItemState.ComboBoxEdit) != DrawItemState.ComboBoxEdit) && (DropDownStyle == ComboBoxStyle.Simple))
				{
					Pen lPen = new Pen(die.BackColor, 1);
					lPen.DashStyle = DashStyle.Dot;
					die.Graphics.DrawRectangle(lPen, die.Bounds);
				}
				else
				{
					if (((die.State & DrawItemState.ComboBoxEdit) != DrawItemState.ComboBoxEdit) && (DropDownStyle != ComboBoxStyle.Simple))
					{
						die.DrawBackground();
					}
				}
				Render(die, (string)Items[die.Index], out mHtmlLinkRegions);
				die.DrawFocusRectangle();
			}
		}
		
		/// <summary>
		/// Raises the <see cref="LinkClicked"/> event.
		/// </summary>
		/// <param name="e">An <see cref="HtmlDropDownList.LinkClickedEventArgs"/> that contains the event data.</param>
		protected void OnLinkClicked(HtmlDropDownList.LinkClickedEventArgs e)
		{
			if (this.LinkClicked != null)
			{
				this.LinkClicked(this, e);
			}
		}
	
		/// <summary>
		/// Handles the <see cref="Control.MouseMove"/> event.
		/// </summary>
		/// <param name="e">A <see cref="MouseEventArgs"/> that contains the event data.</param>
		/// <remarks>
		/// Ensure the <see cref="System.Windows.Forms.Cursor.Current"/> cursor is set to <see cref="Cursors.Default"/> in case
		/// the mouse had been over a link.
		/// </remarks>
		protected override void OnMouseLeave(EventArgs e)
		{
			Cursor.Current = Cursors.Default;
			base.OnMouseLeave (e);
		}
	
		/// <summary>
		/// Handles the <see cref="Control.MouseMove"/> event.
		/// </summary>
		/// <param name="e">A <see cref="MouseEventArgs"/> that contains the event data.</param>
		/// <remarks>
		/// If the mouse moves over a link set the cursor to the <see cref="Cursors.Hand"/>.
		/// </remarks>
		protected override void OnMouseMove(MouseEventArgs e)
		{
			if (LinkRegions != null)
			{
				PointF lPosition = new Point(e.X, e.Y);

				Cursor.Current = Cursors.Default;
				CurrentLinkRegion = null;

				foreach (LinkRegion lLinkRegion in LinkRegions)
				{
					if (lLinkRegion.Bounds.Contains(lPosition) == true)
					{
						Cursor.Current = Cursors.Hand;
						CurrentLinkRegion = lLinkRegion;
						break;
					}
				}
			}
			base.OnMouseMove (e);
		}

		/// <summary>
		/// Adds the specified items to the combo box.
		/// </summary>
		/// <param name="value">An <cref name="ComboBox.ObjectCollection"/> to append to the ComboBox.</param>
		internal void AddItems(ComboBox.ObjectCollection value)
		{
			object[] dest = new object[value.Count];
			value.CopyTo(dest, 0);
			base.AddItemsCore (dest);
		}

		/// <summary>
		/// Converts the HTML color attribute to a Color struct.
		/// </summary>
		/// <param name="color">The HTML color attribute.</param>
		/// <returns>A Color struct equivilent of the color attribute.</returns>
		private Color ParseColor(string color)
		{
			Color lReturn;

			if (color[0] == '#')
			{
				try
				{
					lReturn = Color.FromArgb
						(
						int.Parse(color.Substring(1,2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture),
						int.Parse(color.Substring(3,2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture),
						int.Parse(color.Substring(5,2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture)
						);
				}
				catch (System.ArgumentOutOfRangeException)
				{
					lReturn = ForeColor;
				}
				catch (System.FormatException)
				{
					lReturn = ForeColor;
				}
			}
			else
			{
				lReturn = Color.FromName(color);
				if (lReturn.IsKnownColor == false)
				{
					lReturn = ForeColor;
				}
			}

			return lReturn;
		}

		/// <summary>
		/// Converts the HTML tag to an ElementType enumerated object.
		/// </summary>
		/// <param name="name">The HTML tag to convert.</param>
		/// <returns>The resulting <see cref="ElementType"/>.</returns>
		/// <remarks>
		/// This conversion is case insensitive and matches on the Tag attribute attached to the 
		/// ElementType enum members using System.Reflection.
		/// </remarks>
		private static ElementType ParseElement(string name)
		{
			ElementType					lReturn = ElementType.Other;
			System.Reflection.FieldInfo lFieldInfo;
			TagAttribute[]				lTagAttributes;

			foreach (ElementType lElementType in (ElementType[])Enum.GetValues(typeof(ElementType))) 
			{
				lFieldInfo =  lElementType.GetType().GetField(lElementType.ToString());
				lTagAttributes = (TagAttribute[]) lFieldInfo.GetCustomAttributes(typeof(TagAttribute), false);
				if (lTagAttributes.Length > 0)
				{
					if (string.Compare(lTagAttributes[0].Tag, name, true, CultureInfo.InvariantCulture) == 0)
					{
						lReturn = lElementType;
					}
				}
			}

			return lReturn;
		}

		/// <summary>
		/// Renders the HTML in <paramref name="html"/> using the graphics object in <paramref name="die"/>.
		/// </summary>
		/// <param name="die">A <see cref="DrawItemEventArgs"/> that provides the
		/// <see cref="Graphics"/> object and the <see cref="Rectangle"/> to draw in.</param>
		/// <param name="html">The HTML to render.</param>
		/// <param name="linkRegions">An array of <see cref="LinkRegion"/> objects that define the link areas.</param>
		/// <exception cref="System.Exception">Thrown when nested anchor &lt;A&gt; tags are detected.</exception>
		/// <remarks>
		/// Dummy &lt;html&gt; root tags are added to <paramref name="html"/> to satisfy the <see cref="XmlTextReader"/> class.
		/// <para/>
		/// An <see cref="XmlTextWriter"/> is used to build the <see cref="LinkRegion.InnerHtml"/> property of the <see cref="LinkRegion"/> object.
		/// <para/>
		/// The only XML node types we are interested in are <see cref="XmlNodeType.Element"/> that represent the HTML start tag, 
		/// <see cref="XmlNodeType.EndElement"/> that represent the end tag and <see cref="XmlNodeType.Element"/> that
		/// represents the text.
		/// <para/>
		/// A <see cref="Stack"/> object is used to parse the HTML document tree, the stack can only contain
		/// <see cref="StackItem"/> objects, which contain sufficent information to render the part of the tree.
		/// <para/>
		/// The IMG tag is different from the rest as it is an empty tag (no end tag), so the stack processing 
		/// needs to be different. 
		/// </remarks>
		private void Render(DrawItemEventArgs die, string html, out LinkRegion[] linkRegions)
		{
			StringBuilder			lInnerHtml = new StringBuilder(html.Length);
			LinkRegion				lLinkRegion = new LinkRegion();
			ArrayList				lLinkRegions = new ArrayList();
			float					lOffsetX = 0;
			Stack					lStack = new Stack();
			StackItem				lStackItem = new StackItem(Font, ForeColor);
			XmlTextReader			lXmlReader = null;
			XmlTextWriter			lXmlWriter = null;

			linkRegions = null;

			lStack.Push(lStackItem);

			try 
			{       
				lXmlReader = new XmlTextReader(new StringReader("<html>" + html + "</html>"), HtmlNameTable);
				lXmlReader.WhitespaceHandling = WhitespaceHandling.All;

				lXmlWriter = new XmlTextWriter(new StringWriter(lInnerHtml, CultureInfo.InvariantCulture));

				while (lXmlReader.Read()) 
				{
					switch (lXmlReader.NodeType) 
					{
						case XmlNodeType.Element:
						{
							switch (RenderElement(lXmlReader, lXmlWriter, lStack))
							{		
								case ElementType.Anchor:
									lLinkRegion.Id = ((StackItem)lStack.Peek()).Id;
									lLinkRegion.Bounds = new RectangleF(lOffsetX, die.Bounds.Y, 0, die.Bounds.Height);
									lLinkRegion.InnerText = string.Empty;
									lLinkRegion.InnerHtml = string.Empty;
									break;
								case ElementType.Image:
									lOffsetX += RenderImage((StackItem)lStack.Peek(), die, lOffsetX);
									lStack.Pop();
									break;
							}
							break;
						}
						case XmlNodeType.Whitespace:
						case XmlNodeType.Text:
							lStackItem = ((StackItem)lStack.Peek()).Clone();
							lStackItem.Text = lXmlReader.Value;
							if (lStackItem.IsLink == true)
							{
								lLinkRegion.InnerText += lXmlReader.Value;
								lXmlWriter.WriteString(lXmlReader.Value);
							}
							lOffsetX += RenderString(lStackItem, die, lOffsetX);
							break;
						case XmlNodeType.EndElement:
							if (RenderEndElement(lXmlReader, lXmlWriter, lStack) == ElementType.Anchor)
							{
								lLinkRegion.InnerHtml = lInnerHtml.ToString();
								lLinkRegion.Bounds = new RectangleF(lLinkRegion.Bounds.X, lLinkRegion.Bounds.Y, lOffsetX - lLinkRegion.Bounds.X, lLinkRegion.Bounds.Height);
								lLinkRegions.Add(lLinkRegion);
							}
							break;
					}
				}
			}
			catch (System.Xml.XmlException)
			{
			}
			finally
			{
				lXmlReader.Close();
			}

			linkRegions = (LinkRegion[])lLinkRegions.ToArray(typeof(LinkRegion));
		}

		/// <summary>
		/// Process an Element node type defined by <see cref="ElementType"/>.
		/// </summary>
		/// <param name="xmlReader">The <see cref="XmlTextReader"/> used to process the <c>Element</c> node.</param>
		/// <param name="xmlWriter">The <see cref="XmlTextWriter"/> used to build the innerHtml property.</param> 
		/// <param name="stack">The <see cref="Stack"/> used in parsing the HTML.</param>
		/// <returns>Returns the <see cref="ElementType"/> that was processed.</returns>
		/// <remarks>
		/// Only a few attributes are processed but we attempt to maintain all attributes to be returned in the
		/// <see cref="LinkRegion.InnerHtml"/> property, refer to the default case in the switch statement.
		/// All branches that are not processed by lower methods end up in the default case.
		/// </remarks>
		private ElementType RenderElement(XmlTextReader xmlReader, XmlTextWriter xmlWriter, Stack stack)
		{
			bool lIsEmptyElement = xmlReader.IsEmptyElement;
			StackItem lStackItem = ((StackItem)stack.Peek()).Clone();
			ElementType lReturn = ParseElement(xmlReader.Name);

			switch (lReturn)
			{
				case ElementType.Anchor:
					RenderElementAnchor(xmlReader, stack, lStackItem);
					break;
				case ElementType.Bold:
					lStackItem.Font = new Font(lStackItem.Font, lStackItem.Font.Style | FontStyle.Bold);
					stack.Push(lStackItem);
					goto default;
				case ElementType.Brush:
					RenderElementBrush(xmlReader, stack, lStackItem);
					break;
				case ElementType.Font:
					RenderElementFont(xmlReader, xmlWriter, stack, lStackItem);
					break;
				case ElementType.Image:
					RenderElementImage(xmlReader, xmlWriter, stack, lStackItem);
					break;
				case ElementType.Italic:
					lStackItem.Font = new Font(lStackItem.Font, lStackItem.Font.Style | FontStyle.Italic);
					stack.Push(lStackItem);
					goto default;
				case ElementType.Strikeout:
					lStackItem.Font = new Font(lStackItem.Font, lStackItem.Font.Style | FontStyle.Strikeout);
					stack.Push(lStackItem);
					goto default;
				case ElementType.Underline:
					lStackItem.Font = new Font(lStackItem.Font, lStackItem.Font.Style | FontStyle.Underline);
					stack.Push(lStackItem);
					goto default;
				default:
					if (lStackItem.IsLink == true)
					{
						xmlWriter.WriteStartElement(xmlReader.Name);

						if (xmlReader.HasAttributes)
						{
							if (xmlReader.MoveToFirstAttribute())
							{
								do
								{
									xmlWriter.WriteAttributeString(xmlReader.Name, xmlReader.Value);
								}
								while (xmlReader.MoveToNextAttribute());
							}
						}
					}
					break;
			}
			if (lStackItem.IsLink == true)
			{
				if (lIsEmptyElement == true)
				{
					xmlWriter.WriteEndElement();
				}
			}
			return lReturn;
		}

		/// <summary>
		/// Processes the Anchor tag.
		/// </summary>
		/// <param name="xmlReader">The <see cref="XmlTextReader"/> used to process the <c>Element</c> node.</param>
		/// <param name="stack">The <see cref="Stack"/> used in parsing the HTML.</param>
		/// <param name="stackItem">The <see cref="StackItem"/> that is at the top of the stack.</param>
		/// <exception cref="System.ArgumentException">Thrown when an attempt is made to nest anchor &lt;A&gt; tags is detected.</exception>
		/// <remarks>
		/// A <see cref="StackItem"/> is added to the stack regardless of whether the link is correctly defined
		/// to simplify removing items from the stack when we get to the &lt;/A&gt; tag.
		/// </remarks>
		private void RenderElementAnchor(XmlTextReader xmlReader, Stack stack, StackItem stackItem)
		{
			if (((StackItem)stack.Peek()).IsLink == true)
			{
				ResourceManager lResourceManager = new ResourceManager(typeof(HtmlDropDownList));
				throw new System.ArgumentException(lResourceManager.GetString("NestedAnchorError"));
			}

			if (xmlReader.HasAttributes)
			{
				if (xmlReader.MoveToFirstAttribute())
				{
					do
					{
						switch (xmlReader.Name.ToLower(CultureInfo.InvariantCulture))
						{
							case "id":
								if (xmlReader.ReadAttributeValue())
								{
									if (xmlReader.NodeType == XmlNodeType.Text)
									{
										stackItem.Color = LinkColor;
										stackItem.Font = new Font(stackItem.Font, stackItem.Font.Style | FontStyle.Underline);
										stackItem.Id = xmlReader.Value;
										stackItem.IsLink = true;
									}
								}
								break;
						}
					}
					while (xmlReader.MoveToNextAttribute());
				}
			}
			stack.Push(stackItem);
		}

		/// <summary>
		/// Processes the Brush tag.
		/// </summary>
		/// <param name="xmlReader">The <see cref="XmlTextReader"/> used to process the <c>Element</c> node.</param>
		/// <param name="stack">The <see cref="Stack"/> used in parsing the HTML.</param>
		/// <param name="stackItem">The <see cref="StackItem"/> that is at the top of the stack.</param>
		/// <remarks>
		/// A <see cref="StackItem"/> is added to the stack regardless of whether the link is correctly defined
		/// to simplify removing items from the stack when we get to the &lt;/BRUSH&gt; tag.
		/// </remarks>
		private void RenderElementBrush(XmlTextReader xmlReader, Stack stack, StackItem stackItem)
		{
			if (xmlReader.HasAttributes)
			{
				if (xmlReader.MoveToFirstAttribute())
				{
					do
					{
						switch (xmlReader.Name.ToLower(CultureInfo.InvariantCulture))
						{
							case "type":
								if (xmlReader.ReadAttributeValue())
								{
									stackItem.BrushType = (BrushType)Enum.Parse(typeof(BrushType), xmlReader.Value, true);
								}
								break;
							case "shadowcolor":
								if (xmlReader.ReadAttributeValue())
								{
									stackItem.ShadowColor = ParseColor(xmlReader.Value);
								}
								break;
							case "startingcolor":
								if (xmlReader.ReadAttributeValue())
								{
									stackItem.StartingColor = ParseColor(xmlReader.Value);
								}
								break;
							case "endingcolor":
								if (xmlReader.ReadAttributeValue())
								{
									stackItem.EndingColor = ParseColor(xmlReader.Value);
								}
								break;
							case "gradientangle":
								if (xmlReader.ReadAttributeValue())
								{
									try
									{
										stackItem.GradientAngle = float.Parse(xmlReader.Value, CultureInfo.InvariantCulture);
									}
									catch (System.FormatException)
									{
										stackItem.GradientAngle = 0;
									}
								}
								break;
							case "offset":
								if (xmlReader.ReadAttributeValue())
								{
									stackItem.Offset = float.Parse(xmlReader.Value, CultureInfo.InvariantCulture);
								}
								break;
						}
					}
					while (xmlReader.MoveToNextAttribute());
				}
			}
			stack.Push(stackItem);
		}

		/// <summary>
		/// Processes the Font tag.
		/// </summary>
		/// <param name="xmlReader">The <see cref="XmlTextReader"/> used to process the <c>Element</c> node.</param>
		/// <param name="xmlWriter">The <see cref="XmlTextWriter"/> used to build the innerHtml property.</param> 
		/// <param name="stack">The <see cref="Stack"/> used in parsing the HTML.</param>
		/// <param name="stackItem">The <see cref="StackItem"/> that is at the top of the stack.</param>
		/// <remarks>
		/// A <see cref="StackItem"/> is added to the stack regardless of whether the link is correctly defined
		/// to simplify removing items from the stack when we get to the &lt;/FONT&gt; tag.
		/// </remarks>
		private void RenderElementFont(XmlTextReader xmlReader, XmlTextWriter xmlWriter, Stack stack, StackItem stackItem)
		{
			if (stackItem.IsLink == true)
			{
				xmlWriter.WriteStartElement(xmlReader.Name);
			}

			if (xmlReader.HasAttributes)
			{
				if (xmlReader.MoveToFirstAttribute())
				{
					do
					{
						if (stackItem.IsLink == true)
						{
							xmlWriter.WriteAttributeString(xmlReader.Name, xmlReader.Value);
						}

						switch (xmlReader.Name.ToLower(CultureInfo.InvariantCulture))
						{
							case "face":
								if (xmlReader.ReadAttributeValue())
								{
									if (xmlReader.NodeType == XmlNodeType.Text)
									{
										stackItem.Font = new Font(xmlReader.Value, stackItem.Font.Size, stackItem.Font.Style);
									}
								}
								break;
							case "color":
								if (xmlReader.ReadAttributeValue())
								{
									if (xmlReader.NodeType == XmlNodeType.Text)
									{
										stackItem.Color = ParseColor(xmlReader.Value);
									}
								}
								break;
						}
					}
					while (xmlReader.MoveToNextAttribute());
				}
			}
			stack.Push(stackItem);
		}

		/// <summary>
		/// Processes the Image tag.
		/// </summary>
		/// <param name="xmlReader">The <see cref="XmlTextReader"/> used to process the <c>Element</c> node.</param>
		/// <param name="xmlWriter">The <see cref="XmlTextWriter"/> used to build the innerHtml property.</param> 
		/// <param name="stack">The <see cref="Stack"/> used in parsing the HTML.</param>
		/// <param name="stackItem">The <see cref="StackItem"/> that is at the top of the stack.</param>
		/// <remarks>
		/// As the image tag is always an empty tag so we only add a <see cref="StackItem"/> to the stack if
		/// we need to.  Empty tags require special handling for removing entries from the stack anyway.  All
		/// other tags have entries put on the stack even if they aren't needed to simplify removing entries
		/// from the stack when the end tag is processed.
		/// </remarks>
		private static void RenderElementImage(XmlTextReader xmlReader, XmlTextWriter xmlWriter, Stack stack, StackItem stackItem)
		{
			if (stackItem.IsLink == true)
			{
				xmlWriter.WriteStartElement(xmlReader.Name);
			}

			if (xmlReader.HasAttributes)
			{
				if (xmlReader.MoveToFirstAttribute())
				{
					do
					{
						if (stackItem.IsLink == true)
						{
							xmlWriter.WriteAttributeString(xmlReader.Name, xmlReader.Value);
						}

						switch (xmlReader.Name.ToLower(CultureInfo.InvariantCulture))
						{
							case "src":
								if (xmlReader.ReadAttributeValue())
								{
									if (xmlReader.NodeType == XmlNodeType.Text)
									{
										stackItem.Source = xmlReader.Value;
										stackItem.IsImage = true;
										stack.Push(stackItem);
									}
								}
								break;
						}
					}
					while (xmlReader.MoveToNextAttribute());
				}
			}
		}

		/// <summary>
		/// Process an EndElement node type as defined by <see cref="ElementType"/>.
		/// </summary>
		/// <param name="xmlReader">The <see cref="XmlTextReader"/> used to process the <c>Element</c> node.</param>
		/// <param name="xmlWriter">The <see cref="XmlTextWriter"/> used to build the innerHtml property.</param>
		/// <param name="stack">The <see cref="Stack"/> used in parsing the HTML.</param>
		/// <returns>Returns the <see cref="ElementType"/> that was processed.</returns>
		/// <remarks>
		/// The XmlTextReader will complain about any mismatched EndElements so we don't need to worry
		/// about that here.
		/// </remarks>
		private static ElementType RenderEndElement(XmlTextReader xmlReader, XmlTextWriter xmlWriter, Stack stack)
		{
			ElementType lReturn = ParseElement(xmlReader.Name);

			switch (lReturn)
			{
				case ElementType.Anchor:
				case ElementType.Bold:
				case ElementType.Brush:
				case ElementType.Font:
				case ElementType.Italic:
				case ElementType.Strikeout:
				case ElementType.Underline:
					stack.Pop();
					break;
			}

			if (((StackItem)stack.Peek()).IsLink == true)
			{
				xmlWriter.WriteEndElement();
			}

			return lReturn;
		}

		/// <summary>
		/// Renders an image.
		/// </summary>
		/// <param name="stackItem">A <see cref="StackItem"/> that defines the image to be drawn.</param>
		/// <param name="die">The <see cref="Graphics"/> object to do the drawing.</param>
		/// <param name="offsetX">The <c>X</c> offset to position the image.</param>
		/// <returns>The width the the image drawn.</returns>
		/// <remarks>
		/// The <see cref="Graphics.DrawImage"/> method used will scale the image to fit the height
		/// available.
		/// </remarks>
		private float RenderImage(StackItem stackItem, DrawItemEventArgs die, float offsetX)
		{
			float lReturn;
			Image lImage = null;

			try
			{
				if (stackItem.Source[0] == '#')
				{
					lImage = ImageList.Images[int.Parse(stackItem.Source.Remove(0, 1), CultureInfo.InvariantCulture)];
				}
				else
				{
					lImage = new Bitmap(BaseHref + stackItem.Source);
				}

				if (lImage.Height > die.Bounds.Height)
				{
					lReturn = (float)lImage.Width * ((float)die.Bounds.Height / (float)lImage.Height);
					die.Graphics.DrawImage(lImage, new RectangleF(die.Bounds.X + offsetX, die.Bounds.Y, lReturn, die.Bounds.Height));
				}
				else
				{
					die.Graphics.DrawImage(lImage, new PointF(die.Bounds.X + offsetX, (die.Bounds.Height - lImage.Height) / 2));
					lReturn = lImage.Width;
				}
			}
			catch (System.ArgumentException)
			{
				lReturn = 0;
			}
			finally
			{
				if (lImage != null)
				{
					lImage.Dispose();
				}
			}

			return lReturn;
		}

		/// <summary>
		/// Renders a String.
		/// </summary>
		/// <param name="stackItem">A <see cref="StackItem"/> that defines the string to be drawn.</param>
		/// <param name="die">The <see cref="Graphics"/> object to do the drawing.</param>
		/// <param name="offsetX">The <c>X</c> offset to position the string.</param>
		/// <returns>The width the the string drawn.</returns>
		/// <remarks>
		/// The string drawn by <c>RenderString</c> is the smallest string unit we deal with.
		/// <para/>
		/// The whole string is drawn with the same font attributes.  So although <see cref="Graphics.MeasureCharacterRanges"/>
		/// could handle multiple <see cref="CharacterRange"/> objects here we'll only ever have one.
		/// <para/>
		/// </remarks>
		private float RenderString(StackItem stackItem, DrawItemEventArgs die, float offsetX)
		{
			Brush lBrush;
			RectangleF lLayoutRectangle;

			StringFormat lStringFormat = StringFormat.GenericTypographic;
			lStringFormat.FormatFlags = StringFormatFlags.MeasureTrailingSpaces;
			// Ensure text is truncated as opposed to word wrapped
			lStringFormat.FormatFlags |= StringFormatFlags.NoWrap;  
			lStringFormat.Trimming = StringTrimming.None;

			// Scale the font to fit the ItemHeight supplied
			Font lFont = new Font(stackItem.Font.Name,
								  die.Font.SizeInPoints * (ItemHeight / die.Graphics.MeasureString(stackItem.Text, new Font(stackItem.Font.Name, die.Font.Size)).Height), 
								  stackItem.Font.Style, 
				                  GraphicsUnit.Point);

			die.Graphics.TextRenderingHint = this.TextRenderingHint;

			lStringFormat.SetMeasurableCharacterRanges(new CharacterRange[] {new CharacterRange(0, stackItem.Text.Length)});

			Region[] stringRegions = die.Graphics.MeasureCharacterRanges
				(
				stackItem.Text,
				lFont,
				die.Bounds,
				lStringFormat
				);
			lLayoutRectangle = stringRegions[0].GetBounds(die.Graphics);
			
			if (offsetX + lLayoutRectangle.X < die.Bounds.Width)
			{
				if (lLayoutRectangle.Width == 0)
				{
					lLayoutRectangle = new RectangleF(die.Bounds.Location, die.Graphics.MeasureString(stackItem.Text, stackItem.Font, die.Bounds.Width, lStringFormat));
				}

				lLayoutRectangle.Offset(offsetX, 0);

				switch (stackItem.BrushType)
				{
					case BrushType.LinearGradient:
						lBrush = new LinearGradientBrush(lLayoutRectangle, stackItem.StartingColor, stackItem.EndingColor, stackItem.GradientAngle);
						break;
					case BrushType.Shadow:
						using (Brush lShadowBrush = new SolidBrush(stackItem.ShadowColor)) 
						{
							die.Graphics.DrawString
								(
								stackItem.Text,
								lFont,
								lShadowBrush,
								lLayoutRectangle.X + stackItem.Offset,
								die.Bounds.Y + stackItem.Offset,
								lStringFormat
								);
						}
						goto default;
					default:
						lBrush = new SolidBrush(stackItem.Color);
						break;
				}

				die.Graphics.DrawString
					(
					stackItem.Text,
					lFont,
					lBrush,
					lLayoutRectangle.X,
					die.Bounds.Y,
					lStringFormat
					);

				lBrush.Dispose();
			}
			return lLayoutRectangle.Width;
		}

		/// <summary>
		/// Replace the string <c>s</c> for the link specified by id <c>id</c>.
		/// </summary>
		/// <param name="id">The HTML id of the link.</param>
		/// <param name="s">The innerText or innerHtml to replace.</param>
		/// <param name="method">Specifies whether <c>s</c> should be treated as innerHtml or innerText.</param>
		private void Replace(string id, string s, WriteMethod method)
		{
			bool lIsEmptyElement;
			bool lReplaceText = false;
			StringBuilder lOutputHtml = new StringBuilder(Text.Length + s.Length) ;
			XmlTextReader lXmlReader = new XmlTextReader(new StringReader("<html>" + Text + "</html>"), HtmlNameTable);
			XmlTextWriter lXmlWriter = new XmlTextWriter(new StringWriter(lOutputHtml, CultureInfo.InvariantCulture));

			lXmlReader.WhitespaceHandling = WhitespaceHandling.All;
			lXmlWriter.Formatting = Formatting.None;

			while (lXmlReader.Read())
			{
				switch (lXmlReader.NodeType) 
				{
					case XmlNodeType.Element:
						if (lReplaceText == false)
						{
							if (lXmlReader.IsEmptyElement == true)
							{
								lIsEmptyElement = true; 
							}
							else
							{
								lIsEmptyElement = false; 
							}
							lXmlWriter.WriteStartElement(lXmlReader.Name);
							if (lXmlReader.MoveToFirstAttribute())
							{
								do
								{
									lXmlWriter.WriteAttributeString(lXmlReader.Name, lXmlReader.Value);
									if (string.Compare(lXmlReader.Name, "id", true, CultureInfo.InvariantCulture) == 0 && lXmlReader.Value == id)
									{
										lReplaceText = true;
										if (method == WriteMethod.WriteString)
										{
											lXmlWriter.WriteString(s);
										}
										else
										{
											if (method == WriteMethod.WriteHtml)
											{
												lXmlWriter.WriteRaw(s);
											}
										}
									}
								}
								while (lXmlReader.MoveToNextAttribute());
							}
							if (lIsEmptyElement == true)
							{
								lXmlWriter.WriteEndElement();
							}
						}
						break;
					case XmlNodeType.Whitespace:
					case XmlNodeType.Text:
						if (lReplaceText == false)
						{
							lXmlWriter.WriteString(lXmlReader.Value);
						}
						break;
					case XmlNodeType.EndElement:
						if (ParseElement(lXmlReader.Name) == ElementType.Anchor)
						{
							lReplaceText = false;
						}
						if (lReplaceText == false)
						{
							lXmlWriter.WriteEndElement();
						}
						break;
				}
			}
			lXmlReader.Close();
			lXmlWriter.Close();

			// Remove our dummy "<html>" tags from the beginning and end.
			Items[SelectedIndex] = lOutputHtml.ToString(6, lOutputHtml.Length - 13);
		}

		/// <summary>
		/// Replaces the InnerHtml for the link with the specified id.
		/// </summary>
		/// <param name="id">The <c>id</c> of link to process.</param>
		/// <param name="innerHtml">The text to update the link with.</param>
		public void ReplaceInnerHtml(string id, string innerHtml)
		{
			Replace(id, innerHtml, WriteMethod.WriteHtml);
		}

		/// <summary>
		/// Replaces the InnerText for the link with the specified id.
		/// </summary>
		/// <param name="id">The <c>id</c> of link to process.</param>
		/// <param name="innerText">The html to update the link with.</param>
		public void ReplaceInnerText(string id, string innerText)
		{
			Replace(id, innerText, WriteMethod.WriteString);
		}

		internal void SetDropDownStyle(ComboBoxStyle comboBoxStyle)
		{
			base.DropDownStyle = comboBoxStyle;
		}

		/// <summary>
		/// Handles Windows messages.
		/// </summary>
		/// <param name="m">The Windows <see cref="Message"/> to process. </param>
		/// <remarks>
		/// When the <see cref="DropDownStyle"/> is set to <see cref="ComboBoxStyle.DropDownList"/>
		/// a click in any part of the edit area causes the drop down area to be displayed, which
		/// we don't want to happen as we wouldn't be able to handle our link click
		/// event.
		/// </remarks>
		[System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.LinkDemand, UnmanagedCode=true)]
		protected override void WndProc(ref Message m)
		{
			if (m.Msg == WM_LBUTTONDOWN)
			{
				Point lPosition = PointToClient(new Point(MousePosition.X, MousePosition.Y));
				// If the click occurs over the drop-down button of the HTML combo box control let the
				// base control handle it.
				if (lPosition.X >= Width - SystemInformation.VerticalScrollBarWidth && lPosition.X <= Width)
				{
					base.WndProc (ref m);
				}
				else
				{
					if (CurrentLinkRegion != null)
					{
						OnLinkClicked(new HtmlDropDownList.LinkClickedEventArgs(CurrentLinkRegion.Id, CurrentLinkRegion.InnerText, CurrentLinkRegion.InnerHtml));
					}
				}
			}
			else
			{
				base.WndProc (ref m);
			}
		}

		#endregion

		#region LinkRegion nested class
		/// <summary>
		/// Represents the region of the controls edit area that is a defined as a link.
		/// </summary>
		private class LinkRegion
		{
			/// <summary>
			/// The <see cref="RectangleF"/> that bounds the link.
			/// </summary>
			private RectangleF	mBounds;

			/// <summary>
			/// The HTML id of the &lt;A&gt; tag.
			/// </summary>
			private string		mId;

			/// <summary>
			/// The InnerText of the &lt;A&gt; tag.
			/// </summary>
			private string		mInnerText;

			/// <summary>
			/// The InnerHtml of the &lt;A&gt; tag.
			/// </summary>
			private string		mInnerHtml;

			/// <summary>
			/// Initialise a new instance of the <c>LinkRegion</c> class.
			/// </summary>
			public LinkRegion()
			{
				mBounds = RectangleF.Empty;
				mId = string.Empty;
				mInnerText = string.Empty;
				mInnerHtml = string.Empty;
			}

			/// <summary>
			/// Gets or sets the <see cref="RectangleF"/> that bounds the link.
			/// </summary>
			public RectangleF Bounds
			{
				get { return mBounds; }
				set { mBounds = value; }
			}

			/// <summary>
			/// Get or sets the HTML id of the &lt;A&gt; tag.
			/// </summary>
			public string Id
			{
				get { return mId; }
				set { mId = value; }
			}

			/// <summary>
			/// Gets or sets the InnerText of the &lt;A&gt; tag.
			/// </summary>
			public string InnerText
			{
				get { return mInnerText; }
				set { mInnerText = value; }
			}

			/// <summary>
			/// Gets or sets the InnerHtml of the &lt;A&gt; tag.
			/// </summary>
			public string InnerHtml
			{
				get { return mInnerHtml; }
				set { mInnerHtml = value; }
			}
		}
		#endregion

		#region StackItem nested class
		/// <summary>
		/// Represents the data added to the stack during parsing of the HTML.
		/// </summary>
		private class StackItem
		{
			/// <summary>
			/// Represents the <c>BrushType</c> of the <see cref="StackItem"/>.
			/// </summary>
			private BrushType mBrushType;

			/// <summary>
			/// Represents the <c>Color</c> of the <see cref="StackItem"/>.
			/// </summary>
			private Color	mColor;

			/// <summary>
			/// Represents the <c>EndingColor</c> of the LinearGradientBrush represented by this <see cref="StackItem"/>.
			/// </summary>
			private Color	mEndingColor;

			/// <summary>
			/// The <c>Font</c> of the <see cref="StackItem"/>.
			/// </summary>
			private Font	mFont;

			/// <summary>
			/// The angle, measured in degrees clockwise from the x-axis, of the LinearGradientBrush gradient's orientation line.
			/// </summary>
			private float	mGradientAngle;

			/// <summary>
			/// Represents the value of the <c>Id</c> attribute if the <see cref="StackItem"/> 
			/// represents a link.
			/// </summary>
			private string	mId;

			/// <summary>
			/// Indicates whether the <see cref="StackItem"/> represents a link.
			/// </summary>
			private bool	mIsLink;

			/// <summary>
			/// Indicates whether the <see cref="StackItem"/> represents an image.
			/// </summary>
			private bool	mIsImage;

			/// <summary>
			/// Represents the <c>Offset</c> of the shadow brush represented by this <see cref="StackItem"/>.
			/// </summary>
			private float	mOffset;

			/// <summary>
			/// Represents the <c>ShadowColor</c> of the ShadowBrush represented by this <see cref="StackItem"/>.
			/// </summary>
			private Color	mShadowColor;

			/// <summary>
			/// Represents the <c>Source</c> of the image represented by this <see cref="StackItem"/>.
			/// </summary>
			private string	mSource;

			/// <summary>
			/// Represents the <c>StartingColor</c> of the LinearGradientBrush represented by this <see cref="StackItem"/>.
			/// </summary>
			private Color	mStartingColor;

			/// <summary>
			/// The <c>Text</c> of the <see cref="StackItem"/>.
			/// </summary>
			private string	mText;
 
			/// <summary>
			/// Initialise a new instance of the <c>StackItem</c> class.
			/// </summary>
			/// <param name="font">the <c>Font</c> of the <see cref="StackItem"/>.</param>
			/// <param name="color">The <c>Color</c> of the <see cref="StackItem"/>.</param>
			public StackItem(Font font, Color color)
			{
				mBrushType = BrushType.Default;
				mColor = color;
				mEndingColor = Color.Empty;
				mFont = font;
				mId = string.Empty;
				mSource = string.Empty;
				mStartingColor = Color.Empty;
				mText = string.Empty;
			}

			/// <summary>
			/// Gets or sets the <c>BrushType</c> of the <see cref="StackItem"/>.
			/// </summary>
			public BrushType BrushType
			{
				get { return mBrushType; }
				set { mBrushType = value; }
			}

			/// <summary>
			/// Gets or sets the <c>Color</c> of the <see cref="StackItem"/>.
			/// </summary>
			public Color Color
			{
				get { return mColor; }
				set { mColor = value; }
			}

			/// <summary>
			/// Gets or sets the <c>EndingColor</c> of the <see cref="StackItem"/>.
			/// </summary>
			public Color EndingColor
			{
				get { return mEndingColor; }
				set { mEndingColor = value; }
			}

			/// <summary>
			/// Gets or sets the angle, measured in degrees clockwise from the x-axis, of the LinearGradientBrush gradient's orientation line.
			/// </summary>
			/// <remarks>
			/// A value of 90 degrees causes a horizontal gradient a value or 0 causes a vertical gradient.
			/// </remarks>
			public float GradientAngle
			{
				get { return mGradientAngle; }
				set { mGradientAngle = value; }
			}

			/// <summary>
			/// Gets or sets the value of the <c>Id</c> attribute if the <see cref="StackItem"/> represents a link.
			/// </summary>
			public string Id
			{
				get { return mId; }
				set { mId = value; }
			}

			/// <summary>
			/// Gets or sets a value that is indicates whether the <see cref="StackItem"/> represents an image.
			/// </summary>
			public bool IsImage
			{
				get { return mIsImage; }
				set { mIsImage = value; }
			}

			/// <summary>
			/// Gets a value that is true if the <see cref="StackItem"/> represents a link.
			/// </summary>
			public bool IsLink
			{
				get { return mIsLink; }
				set { mIsLink = value; }
			}

			/// <summary>
			/// Gets or sets the <c>Font</c> of the <see cref="StackItem"/>.
			/// </summary>
			public Font Font
			{
				get { return mFont; }
				set { mFont = value; }
			}

			/// <summary>
			/// Gets or sets the <c>Offset</c> of the shadow brush represented by this <see cref="StackItem"/>.
			/// </summary>
			public float Offset
			{
				get { return mOffset; }
				set { mOffset = value; }
			}

			/// <summary>
			/// Represents the <c>ShadowColor</c> of the ShadowBrush represented by this <see cref="StackItem"/>.
			/// </summary>
			public Color ShadowColor
			{
				get { return mShadowColor; }
				set { mShadowColor = value; }
			}

			/// <summary>
			/// Gets or sets the <c>Source</c> of the image represented by this <see cref="StackItem"/>.
			/// </summary>
			public string Source
			{
				get { return mSource; }
				set { mSource = value; }
			}

			/// <summary>
			/// Gets or sets the <c>StartingColor</c> of the <see cref="StackItem"/>.
			/// </summary>
			public Color StartingColor
			{
				get { return mStartingColor; }
				set { mStartingColor = value; }
			}

			/// <summary>
			/// Gets or sets the <c>Text</c> of the <see cref="StackItem"/>.
			/// </summary>
			public string Text
			{
				get { return mText; }
				set { mText = value; }
			}

			/// <summary>
			/// Creates an identical copy of the <see cref="StackItem"/> except for the image <see cref="Source"/> field.
			/// </summary>
			/// <returns>A shallow copy of the <see cref="StackItem"/> except for the Source.</returns>
			/// <remarks>
			/// The <see cref="Source"/> is never carried forward as the IMG tag can not contain any other tags.
			/// </remarks>
			public StackItem Clone()
			{
				StackItem lReturn;
				lReturn = (StackItem)base.MemberwiseClone();
				lReturn.Source = string.Empty;
				return lReturn;
			}
		}
		#endregion 

		#region LinkClickedEventArgs nested class

		/// <summary>
		/// Provides data for the <see cref="LinkClicked"/> event of the <see cref="HtmlDropDownList"/>.
		/// </summary>
		public class LinkClickedEventArgs : EventArgs
		{
			/// <summary>
			/// The value of the id attribute for the link that was clicked.
			/// </summary>
			private readonly string mId;

			/// <summary>
			/// The text between the start and end anchor tags that was clicked.
			/// </summary>
			private readonly string mInnerText;

			/// <summary>
			/// The HTML between the start and end anchor tags that was clicked.
			/// </summary>
			private readonly string mInnerHtml;

			/// <summary>
			/// Initialise a new instance of the <see cref="LinkClickedEventArgs"/> class.
			/// </summary>
			/// <param name="id">The value of the id attribute for link that was clicked.</param>
			/// <param name="innerText">The text between the start and end anchor tags.</param>
			/// <param name="innerHtml">The HTML between the start and end anchor tags.</param>
			public LinkClickedEventArgs(string id, string innerText, string innerHtml)
			{
				mId = id;
				mInnerText = innerText;
				mInnerHtml = innerHtml;
			}

			/// <summary>
			/// Gets the value of the id attribute for the link that was clicked.
			/// </summary>
			public string Id
			{
				get { return mId; }
			}

			/// <summary>
			/// Gets the text between the start and end anchor tags that was clicked.
			/// </summary>
			public string InnerText
			{
				get { return mInnerText; }
			}

			/// <summary>
			/// Gets the HTML between the start and end anchor tags that was clicked.
			/// </summary>
			public string InnerHtml
			{
				get { return mInnerHtml; }
			}
		}
		#endregion
	}
}